<?php /** @noinspection DuplicatedCode */

namespace app\common\library;

use Curl\Curl;
use think\facade\Log;

/**
 * 直播工具类
 * Class BLiveTools
 * @package app\common\library
 */
class BLiveTools
{
    // 默认配置
    protected array $config = [
        'httpTimeout' => 10, // 请求超时时间
    ];

    /**
     * Constructor.
     * @param array $config
     */
    public function __construct(array $config)
    {
        $this->config = array_merge($this->config, $config);
    }

    /**
     * 获取配置信息
     */
    public function getConfig(): array
    {
        return $this->config;
    }

    /**
     * 发送一次Get请求
     * @param $url
     * @param array $queryParams
     * @param array $header
     * @return false|mixed
     */
    public function doGetRequest($url, array $queryParams = [], array $header = [])
    {
        // 拼装请求参数
        $url = $url . '?' . http_build_query($queryParams);

        // 发起一次Curl请求
        $curl = new Curl();
        $curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
        $curl->setOpt(CURLOPT_SSL_VERIFYHOST, false);
        $curl->setOpt(CURLOPT_TIMEOUT, $this->config['httpTimeout']);
        $curl->setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36');

        // 默认请求头
        $reqHeaders = [
            'Accept'          => 'application/json, text/plain, */*',
            'Accept-Language' => 'zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7',
            'Cache-Control'   => 'no-cache',
            'Pragma'          => 'no-cache',
        ];

        // 合并然后设置请求头
        $reqHeaders = array_merge($reqHeaders, $header);
        foreach ($reqHeaders as $key => $value) {
            $curl->setHeader($key, $value);
        }

        // 发送一次Get请求
        $curlResponse = $curl->get($url);
        if (!$curlResponse->isSuccess()) {
            $this->log(sprintf(
                '请求失败：[%s] %s',
                $curlResponse->getErrorCode(),
                $curlResponse->getErrorMessage()
            ));
            return false;
        }

        // 回来的响应体不能为空
        if (empty($curlResponse->getResponse())) {
            $this->log(sprintf("返回异常：[%s] 响应体为空", $curlResponse->getHttpStatus()));
            return false;
        }

        // 尝试解开Json
        try {
            $data = json_decode($curlResponse->getResponse(), true);
            if (json_last_error()) {
                $this->log(sprintf("解析失败：[%s] %s", json_last_error(), json_last_error_msg()));
                return false;
            }
            // 数据返回
            return $data;
        } catch (\Throwable $throwable) {
            $this->log(sprintf("解析失败：[%s] %s", $throwable->getCode(), $throwable->getMessage()));
            return false;
        }

    }

    /**
     * 保存日志信息
     * @param $content
     * @return void
     */
    public function log($content)
    {
        Log::record($content, 'liveTools');
    }

    /**
     * 获取房间正在进行的互动玩法
     * @param $roomId
     * @return false|array
     */
    public function ApiGetRoomGameInfo($roomId)
    {
        // 组装接口请求
        $url = 'https://api.live.bilibili.com/xlive/open-platform/v1/gameInterface/getInfoByRoom';
        $queryParams = ['room_id' => $roomId,];
        $header = [
            'Referer' => sprintf("https://live.bilibili.com/%s", $roomId),
            'Origin'  => 'https://live.bilibili.com',
        ];

        // 发送请求
        if (!$response = $this->doGetRequest($url, $queryParams, $header)) {
            return false;
        }

        // 如果 ResponseCode=7000 则返回空数组
        if (isset($response['code']) && ($response['code'] === 7000)) {
            return [];
        }

        // 如果等于0则返回游戏列表
        if (isset($response['code']) && ($response['code'] === 0)) {
            $listGame = $response['data']['game_list'];
            return $listGame ? array_column($listGame, null, 'game_code') : [];
        }

        return false;
    }

    /**
     * 获取房间的简要信息
     * 可以获取到UID/公告/主播昵称
     * @param $roomId
     * @return array|false
     */
    public function ApiGetRoomNews($roomId)
    {
        // 组装接口请求
        $url = 'https://api.live.bilibili.com/room_ex/v1/RoomNews/get';
        $queryParams = ['roomid' => $roomId,];
        $header = [
            'Referer' => sprintf("https://live.bilibili.com/%s", $roomId),
            'Origin'  => 'https://live.bilibili.com',
        ];

        // 发送请求
        if (!$response = $this->doGetRequest($url, $queryParams, $header)) {
            return false;
        }

        if (isset($response['code']) && ($response['code'] === 0)) {
            return $response['data'];
        }

        return false;
    }

    /**
     * 获取房间的初始化信息
     * 可以获取主播被封禁状态/开播状态/开播时间
     * @param $roomId
     * @return false|mixed
     */
    public function ApiGetRoomInit($roomId)
    {
        // 组装接口请求
        $url = 'https://api.live.bilibili.com/room/v1/Room/room_init';
        $queryParams = ['id' => $roomId,];
        $header = [
            'Referer' => sprintf("https://live.bilibili.com/%s", $roomId),
            'Origin'  => 'https://live.bilibili.com',
        ];

        // 发送请求
        if (!$response = $this->doGetRequest($url, $queryParams, $header)) {
            return false;
        }

        if (isset($response['code']) && ($response['code'] === 0)) {
            return $response['data'];
        }

        return false;
    }

    /**
     * 获取房间最详细的信息
     * @param $roomId
     * @return false|mixed
     */
    public function ApiGetInfoByRoom($roomId)
    {
        // 组装接口请求
        $url = 'https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom';
        $queryParams = ['room_id' => $roomId,];
        $header = [
            'Referer' => sprintf("https://live.bilibili.com/%s", $roomId),
            'Origin'  => 'https://live.bilibili.com',
        ];

        // 发送请求
        if (!$response = $this->doGetRequest($url, $queryParams, $header)) {
            return false;
        }

        if (isset($response['code']) && ($response['code'] === 0)) {
            return $response['data'];
        }

        return false;
    }

    /**
     * 获取互动玩法详情
     * @param $gameId
     * @return false|mixed
     */
    public function ApiQueryAppDetail($gameId)
    {
        // 组装接口请求
        $url = 'https://api.live.bilibili.com/xlive/virtual-interface/v2/app/queryAppDetail';
        $queryParams = ['app_id' => $gameId,];
        $header = [
            'Referer' => 'https://play-live.bilibili.com/',
            'Origin'  => 'https://play-live.bilibili.com',
        ];

        // 发送请求
        if (!$response = $this->doGetRequest($url, $queryParams, $header)) {
            return false;
        }

        // 如果 ResponseCode=0 则返回游戏详情
        if (isset($response['code']) && ($response['code'] === 0)) {
            return $response['data']['app_info'];
        }

        return false;
    }

    /**
     * 获取直播间主播信息
     * @param $roomId
     * @return false|mixed
     */
    public function ApiGetAnchorInRoom($roomId)
    {
        // 组装接口请求
        $url = 'https://api.live.bilibili.com/live_user/v1/UserInfo/get_anchor_in_room';
        $queryParams = ['roomid' => $roomId,];
        $header = [
            'Referer' => sprintf("https://live.bilibili.com/%s", $roomId),
            'Origin'  => 'https://live.bilibili.com',
        ];

        // 发送请求
        if (!$response = $this->doGetRequest($url, $queryParams, $header)) {
            return false;
        }

        // 如果 ResponseCode=0 则返回主播信息
        if (isset($response['code']) && ($response['code'] === 0)) {
            return $response['data']['info'];
        }

        return false;
    }

    /**
     * 获取直播分区信息
     * @return false|mixed
     */
    public function ApiGetLiveAreaList()
    {
        // 组装接口请求
        $url = 'https://api.live.bilibili.com/xlive/web-interface/v1/index/getWebAreaList';
        $queryParams = ['source_id' => 2,];
        $header = [
            'Referer' => sprintf("https://live.bilibili.com"),
            'Origin'  => 'https://live.bilibili.com',
        ];

        // 发送请求
        if (!$response = $this->doGetRequest($url, $queryParams, $header)) {
            return false;
        }

        // 如果 ResponseCode=0 则返回分区列表
        if (isset($response['code']) && ($response['code'] === 0)) {
            return $response['data']['data'];
        }

        return false;
    }

    /**
     * 获取首页SSR渲染数据
     * @param $roomId
     * @return false|mixed
     */
    public function ApiGetLiveHomeData($roomId)
    {
        // 组装接口请求
        $url = sprintf("https://live.bilibili.com/%s", $roomId);

        // 这里是静态手动获取
        $curl = new Curl();
        $curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
        $curl->setOpt(CURLOPT_SSL_VERIFYHOST, false);
        $curl->setOpt(CURLOPT_TIMEOUT, $this->config['httpTimeout']);
        $curl->setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36');

        // 发送一次Get请求
        $curlResponse = $curl->get($url);
        if (!$curlResponse->isSuccess()) {
            $this->log(sprintf(
                '请求失败：[%s] %s',
                $curlResponse->getErrorCode(),
                $curlResponse->getErrorMessage()
            ));
            return false;
        }

        // 显示请求结果
        $response = $curlResponse->getResponse();
        if (preg_match('/<script>window.__NEPTUNE_IS_MY_WAIFU__\s?=(.*?)<\/script>/', $response, $match)) {
            $infoAll = json_decode($match[1], true);
            if (!json_last_error()) {
                return $infoAll;
            }
        }

        // 请求或解析失败
        return false;
    }

    /**
     * 获取直播间开播状态
     * @param $roomId
     * @return false|mixed
     */
    public function ApiGetRoomPlayInfo($roomId)
    {
        // 组装接口请求
        $url = 'https://api.live.bilibili.com/xlive/web-room/v2/index/getRoomPlayInfo';
        $queryParams = [
            'room_id'  => $roomId,
            'protocol' => '0,1',
            'format'   => '0,1,2',
            'codec'    => '0,1,2',
            'qn'       => '0',
            'platform' => 'web',
            'ptype'    => '8', 'dolby' => '5',
            'panorama' => '1'
        ];
        $header = [
            'Referer' => sprintf("https://live.bilibili.com/%s", $roomId),
            'Origin'  => 'https://live.bilibili.com',
        ];

        // 发送请求
        if (!$response = $this->doGetRequest($url, $queryParams, $header)) {
            return false;
        }

        // 如果 ResponseCode=0 则返回主播信息
        if (isset($response['code']) && ($response['code'] === 0)) {
            return $response['data'];
        }

        return false;
    }

    /**
     * 读取视频的Cid信息
     * @param $bvNumber
     * @return false|mixed
     */
    public function ApiQueryVideoCid($bvNumber)
    {
        // 组装接口请求
        $url = 'https://api.bilibili.com/x/player/pagelist';
        $queryParams = ['bvid' => $bvNumber];
        $header = [
            'Referer' => sprintf("https://www.bilibili.com/video/%s", $bvNumber),
            'Origin'  => 'https://www.bilibili.com',
        ];

        // 发送请求
        if (!$response = $this->doGetRequest($url, $queryParams, $header)) {
            return false;
        }

        // 如果 ResponseCode=0 则返回主播信息
        if (isset($response['code']) && ($response['code'] === 0)) {
            return $response['data'];
        }

        return false;
    }

    /**
     * 读取视频的可播放流信息
     * @param $cid
     * @param $bvNumber
     * @return false|mixed
     */
    public function ApiQueryVideoStream($cid, $bvNumber)
    {
        // 组装接口请求
        $url = 'https://api.bilibili.com/x/player/playurl';
        $queryParams = ['fnval' => 80, 'cid' => $cid, 'bvid' => $bvNumber];
        $header = [
            'Referer' => sprintf("https://www.bilibili.com/video/%s", $bvNumber),
            'Origin'  => 'https://www.bilibili.com',
        ];

        // 发送请求
        if (!$response = $this->doGetRequest($url, $queryParams, $header)) {
            return false;
        }

        // 如果 ResponseCode=0 则返回主播信息
        if (isset($response['code']) && ($response['code'] === 0)) {
            return $response['data'];
        }

        return false;
    }
}
